package com.clp.protocol.core.server;

import com.clp.protocol.core.async.NettyAsyncSupport;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.util.concurrent.DefaultPromise;
import io.netty.util.concurrent.Promise;

import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.ScheduledExecutorService;

public class NettyServer implements NettyAsyncSupport, Closeable {
    private final EventLoopGroup bossGroup;
    private final EventLoopGroup workerGroup;

    public NettyServer() {
        this.bossGroup = new NioEventLoopGroup();
        this.workerGroup = new NioEventLoopGroup();
    }

    public ChannelFuture bind(int port, ChannelInitializer<SocketChannel> childInitializer) {
        return bind(port, null, childInitializer);
    }

    public ChannelFuture bind(int port, ChannelInitializer<ServerSocketChannel> initializer,
                              ChannelInitializer<SocketChannel> childInitializer) {
        ServerBootstrap serverBootstrap = newServerBootstrap();
        if (initializer != null) {
            serverBootstrap.handler(initializer);
        }
        if (childInitializer != null) {
            serverBootstrap.childHandler(childInitializer);
        }
        return serverBootstrap.bind(port);
    }

    private ServerBootstrap newServerBootstrap() {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .option(ChannelOption.TCP_NODELAY, true);
        return serverBootstrap;
    }

    public ChannelFuture bind(String address, int port, ChannelInitializer<SocketChannel> childInitializer) {
        return bind(address, port, null, childInitializer);
    }

    public ChannelFuture bind(String address, int port, ChannelInitializer<ServerSocketChannel> initializer,
                              ChannelInitializer<SocketChannel> childInitializer) {
        ServerBootstrap serverBootstrap = newServerBootstrap().localAddress(address, port);
        if (initializer != null) {
            serverBootstrap.handler(initializer);
        }
        if (childInitializer != null) {
            serverBootstrap.childHandler(childInitializer);
        }
        return serverBootstrap.childHandler(childInitializer).bind();
    }

    @Override
    public <T> Promise<T> createPromise(Class<T> clazz) {
        return new DefaultPromise<>(bossGroup.next());
    }

    @Override
    public ScheduledExecutorService scheduledExecutorService() {
        return bossGroup;
    }

    public boolean isClosed() {
        return bossGroup.isShutdown() && workerGroup.isShutdown();
    }

    @Override
    public void close() throws IOException {
        shutdown(bossGroup);
        shutdown(workerGroup);
    }

    private static void shutdown(EventLoopGroup group) {
        if (!group.isShutdown()) {
            group.shutdownGracefully();
        }
    }
}
